package com.jinke.api.modules.app.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.jinke.api.common.api.CommonResult;
import com.jinke.api.common.exception.Asserts;
import com.jinke.api.common.util.SurchargeTool;
import com.jinke.api.modules.app.config.NoModelDataListener;
import com.jinke.api.modules.app.config.PriceSurchargeImportListener;
import com.jinke.api.modules.app.controller.res.PriceSurchargeImportDto;
import com.jinke.api.modules.app.controller.res.ZipCodeImportDto;
import com.jinke.api.modules.app.model.PriceSurcharge;
import com.jinke.api.modules.app.mapper.PriceSurchargeMapper;
import com.jinke.api.modules.app.response.SelfDefinedPriceResponse;
import com.jinke.api.modules.app.service.PriceSurchargeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jinke.api.modules.base.constant.Constants;
import com.jinke.api.modules.base.constant.RedisKeyConstants;
import com.jinke.api.modules.third.ezeeship.model.request.EstimateRateRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 自定义价格-附加费表 服务实现类
 * </p>
 *
 * @author husuruo
 * @since 2024-07-30
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PriceSurchargeServiceImpl extends ServiceImpl<PriceSurchargeMapper, PriceSurcharge> implements PriceSurchargeService {

    private final PriceSurchargeImportListener priceSurchargeImportListener;

    private final NoModelDataListener noModelDataListener;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @Author: hhp
     * @Description: 导入price surcharge
     * @Data: 2024/7/30 11:43
     * @param   importDto 导入实体
     * @return: com.jinke.api.common.api.CommonResult<java.lang.String>
     * @throws: IOException
     **/
    @Override
    @Transactional
    public CommonResult<String> importPriceSurcharge(PriceSurchargeImportDto importDto) throws IOException {
        InputStream inputStream = importDto.getFile().getInputStream();

        ExcelReaderBuilder excelReaderBuilder = EasyExcel.read(inputStream, PriceSurchargeImportDto.PriceSurchargeItem.class, priceSurchargeImportListener)
                // 需要读取合并单元格信息 默认不读取
                /*.extraRead(CellExtraTypeEnum.MERGE)*/;
        ExcelReaderSheetBuilder sheet = excelReaderBuilder.sheet()
                .headRowNumber(1);// 表头有1行
        List<PriceSurchargeImportDto.PriceSurchargeItem> list = sheet.doReadSync();
        // 重置index
        priceSurchargeImportListener.setCurIndex(0);

        if (CollectionUtil.isEmpty(list)) {
            Asserts.fail("未检测到任何数据");
        }

        List<PriceSurcharge> savePriceSurchargeList = Lists.newArrayList();

        StringBuilder errMsg = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            // TODO 校验数据... 如果步校验可以在listener里面处理数据，不需要再这里再遍历一遍
            PriceSurchargeImportDto.PriceSurchargeItem priceRuleItem = list.get(i);

            if (StrUtil.isNotBlank(priceRuleItem.getZones())) {
                String zones = priceRuleItem.getZones();
                if (zones.contains("-")) {
                    priceRuleItem.setZones(zones.replaceAll("-", ","));
                    /*priceRuleItem.setZones(Arrays.stream(zones.split("-")).map(item -> {
                        return "zone" + item;
                    }).collect(Collectors.joining(",")));*/
                }
                zones = priceRuleItem.getZones();
                if (zones.contains("7+")) {
                    // + 是正则特殊字符 需要用\\转义一下
                    priceRuleItem.setZones(zones.replaceAll("7\\+", "7,8,9"));
                }
            }

            PriceSurcharge priceSurcharge = new PriceSurcharge();
            BeanUtils.copyProperties(priceRuleItem, priceSurcharge);
            // 取excel表里的serviceCode 不将每一项设置成 页面传过来的serviceCode
            //priceSurcharge.setServiceCode(importDto.getServiceCode());
            // 保留2位小数
            //priceSurcharge.setRate(priceSurcharge.getRate().setScale(2, RoundingMode.UP));
            savePriceSurchargeList.add(priceSurcharge);
        }

        if (CollectionUtil.isNotEmpty(savePriceSurchargeList)) {
            boolean result = this.saveBatch(savePriceSurchargeList);
            if (!result) {
                Asserts.fail("保存价格表失败");
            }
        }

        errMsg.append(StrUtil.format("一共{}条数据，成功导入{}条数据，导入失败数据{}条", list.size(), savePriceSurchargeList.size(), list.size() - savePriceSurchargeList.size()));
        return CommonResult.success(errMsg.toString());
    }
    /**
     * 根据包裹信息获取附加费
     *
     * @param request
     * @return
     */
    @Override
    public List<PriceSurcharge> getRateFromPriceSurcharge(EstimateRateRequest request, String zone) {
        QueryWrapper<PriceSurcharge> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(zone!=null,"zones", zone)
                .eq(StringUtils.isNotBlank( request.getServiceCode()),"service_code", request.getServiceCode());

        List<PriceSurcharge> priceSurcharges =  list(queryWrapper);


        return priceSurcharges;
    }

    /**
     * 导入邮编
     * @param importDto
     * @return
     * @throws IOException
     */
    @Override
    public CommonResult<String> importZipCode(ZipCodeImportDto importDto) throws IOException {
        InputStream inputStream = importDto.getFile().getInputStream();
        // 读取excel 放入redis缓存
        String key = "";
        switch (importDto.getRedisKey()) {
            //US 48 Zip表单 邮编前缀
            case "US48":
                key = RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.US_48_ZIP_CODE_PREFIX;
                break;
            // US 48 Zip DAS Extended表单 邮编前缀
            case "DAS":
                key = RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.DAS_ZIP_CODE_PREFIX;
                break;
            //HI-夏威夷 邮编前缀
            case "HI":
                key = RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.HI_ZIP_CODE_PREFIX;
                break;
            // AK-阿拉斯加 邮编前缀
            case "AK":
                key = RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.AK_ZIP_CODE_PREFIX;
                break;
            // Remote US 48 Zip表单 邮编前缀
            case "EAS":
                key = RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.EAS_ZIP_CODE_PREFIX;
                break;
            default:
                break;
        }
        if (StrUtil.isBlank(key)) {
            Asserts.fail("key不匹配");
        }
        if (!Optional.ofNullable(importDto.getSheetNo()).isPresent() || importDto.getSheetNo() < 0) {
            Asserts.fail("无效的sheetNo");
        }
        noModelDataListener.setRedisKey(key);
        // 一个文件一个reader
        ExcelReader excelReader = EasyExcel.read(inputStream, noModelDataListener).build();

        // 构建一个sheet 这里可以指定名字或者no
        ReadSheet readSheet = EasyExcel.readSheet(importDto.getSheetNo()).build();
        // 读取一个sheet
        excelReader.read(readSheet);
        // 重置list
        noModelDataListener.setRedisKey(null);
        noModelDataListener.setDataList(Lists.newArrayList());

        return CommonResult.success("ok");
    }

    /**
     * 距离收费项
     *  @param serviceCode 服务代码
     * @param stringBuilder 字符串构建器
     * @param tipPriceSurchargeList 价格附加费列表
     * @param groupedByItem 按项目分组的映射
     * @param zipCode 邮编
     * @param isResidential 是否为住宅地址
     */
    @Override
    public void checkDistanceSurcharge(String serviceCode, StringBuilder stringBuilder,
                                        List<PriceSurcharge> tipPriceSurchargeList, Map<String, List<PriceSurcharge>> groupedByItem,
                                        String zipCode, Boolean isResidential) {
        // 是否超远 标远 1 超远 2 遥远 Remote US 48 Zip 3 ak 4 hi 5  0 是正常
        Integer isOverDistance = this.checkDistance(zipCode);

        stringBuilder.append("超远 ").append(isOverDistance);

        String distanceItem;
        List<PriceSurcharge> remote;

        switch (isOverDistance) {
            case 5:
                stringBuilder.append("--超远 HAWAII ").append(zipCode);
                distanceItem = getDistanceItem(groupedByItem, Constants.HAWAII, isResidential);
                remote = groupedByItem.get(distanceItem);
                validSurcharge(tipPriceSurchargeList, remote, serviceCode, null, distanceItem);
                break;
            case 4:
                stringBuilder.append("--超远 ALASKA ").append(zipCode);
                distanceItem = getDistanceItem(groupedByItem, Constants.ALASKA, isResidential);
                remote = groupedByItem.get(distanceItem);
                validSurcharge(tipPriceSurchargeList, remote, serviceCode, null, distanceItem);
                break;
            case 3:
                stringBuilder.append("--超远 remote US 48 ").append(zipCode);
                distanceItem = getDistanceItem(groupedByItem, Constants.US_48, isResidential);
                remote = groupedByItem.get(distanceItem);
                validSurcharge(tipPriceSurchargeList, remote, serviceCode, null, distanceItem);
                break;
            case 2:
                stringBuilder.append("--超远 das ").append(zipCode);
                distanceItem =isResidential? Constants.DAS_EXTENDED_RESIDENTIAL : Constants.DAS_EXTENDED_COMMERCIAL;
                remote = groupedByItem.get(distanceItem);
                validSurcharge(tipPriceSurchargeList, remote, serviceCode, null, distanceItem);
                break;
            case 1:
                stringBuilder.append("--超远 das-stadard ").append(zipCode);
                distanceItem =isResidential? Constants.DAS_STANDARD_RESIDENTIAL : Constants.DAS_STANDARD_COMMERCIAL;
                remote = groupedByItem.get(distanceItem);
                validSurcharge(tipPriceSurchargeList, remote, serviceCode, null, distanceItem);
                break;
            case 0:
                System.out.println("普通距离");
                break;
            default:
                break;
        }
    }

    /**
     * 重量收费项
     *
     * @param zone
     * @param serviceCode
     * @param stringBuilder
     * @param tipPriceSurchargeList
     * @param groupedByItem
     * @param parcelWeight
     */
    @Override
    public void checkWeightSurcharge(String zone, String serviceCode, StringBuilder stringBuilder, List<PriceSurcharge> tipPriceSurchargeList, Map<String, List<PriceSurcharge>> groupedByItem, BigDecimal parcelWeight) {
        //是否超重
        boolean isOverWeight = SurchargeTool.checkWeight(parcelWeight);
        stringBuilder.append("超重" + isOverWeight);
        if (isOverWeight) {
            stringBuilder.append("--超重" + parcelWeight + "lb");
            List<PriceSurcharge> overWeightSurcharge = groupedByItem.get(Constants.OVERWEIGHT);
            //生效
            validSurcharge(tipPriceSurchargeList, overWeightSurcharge, serviceCode, zone, Constants.OVERWEIGHT);
        }
    }

    /**
     * 大小收费项
     *
     * @param selfDefinedPriceResponse
     * @param zone
     * @param serviceCode
     * @param stringBuilder
     * @param tipPriceSurchargeList
     * @param groupedByItem
     * @param parcelLength
     * @param parcelHeight
     * @param parcelWidth
     * @param parcelWeight
     * @param isResidential
     * @return
     */
    @Override
    public Integer checkOversizeSurcharge(SelfDefinedPriceResponse selfDefinedPriceResponse, String zone, String serviceCode, StringBuilder stringBuilder, List<PriceSurcharge> tipPriceSurchargeList, Map<String, List<PriceSurcharge>> groupedByItem, BigDecimal parcelLength, BigDecimal parcelHeight, BigDecimal parcelWidth, BigDecimal parcelWeight, Boolean isResidential) {

        //是否超大
        Integer isOverSize = SurchargeTool.checkSize(parcelWeight, parcelLength, parcelWidth, parcelHeight);
        selfDefinedPriceResponse.setIsOverSize(isOverSize);
        stringBuilder.append("超大"+isOverSize+"长"+ parcelLength
                +"宽"+ parcelWidth
                +"高"+ parcelHeight);

        String oversizeItem="";
        //大小
        switch (isOverSize) {
            case 3:
                List<PriceSurcharge> super_oversize = groupedByItem.get(Constants.HUGE_SIZE);
                stringBuilder.append("--超大件限制");
                //生效
                validSurcharge(tipPriceSurchargeList, super_oversize, serviceCode,null,Constants.HUGE_SIZE);
                break;
            case 2:
                stringBuilder.append("--超大包裹费");

                List<PriceSurcharge>  oversize;
                if (isResidential){
                    oversizeItem=Constants.OVERSIZE_RESI;
                }else {
                    oversizeItem=Constants.OVERSIZE_COM;

                }
                oversize = groupedByItem.get(oversizeItem);
                //生效
                validSurcharge(tipPriceSurchargeList, oversize, serviceCode, zone,oversizeItem);

                break;
            case 1:
                stringBuilder.append("--超大额外要收钱");

                List<PriceSurcharge> Additional = groupedByItem.get(Constants.OVERSIZE_ADD);
                //生效
                validSurcharge(tipPriceSurchargeList, Additional, serviceCode, zone,Constants.OVERSIZE_ADD);

                break;
            case 0:
                stringBuilder.append("--非超大只是普通包裹");
                break;
            default:
                System.out.println("无效的尺寸类别");
        }
        return isOverSize;
    }

    /**
     * 处理峰值相关的条件
     *
     * @param groupedByItem    分组后的项目数据
     * @param tipPriceSurchargeList  附加费列表
     * @param serviceCode    服务代码
     * @param zone    区域
     */
    @Override
    public void handlePeakConditions(Map<String, List<PriceSurcharge>> groupedByItem, List<PriceSurcharge> tipPriceSurchargeList, String serviceCode, String zone, Integer isOverSize, Boolean isResidential) {
        List<PriceSurcharge> list;

        // 处理峰值且超尺寸不为 0 的情况
        if (isOverSize!= 0) {
            list = groupedByItem.get(Constants.PEAK_OVERSIZE_CHARGE);
            validSurcharge(tipPriceSurchargeList, list, serviceCode, zone,Constants.PEAK_OVERSIZE_CHARGE);
        }

        // 处理峰值且为住宅的情况
        if (isResidential) {
            list = groupedByItem.get(Constants.PEAK_RESIDENTIAL_CHARGE);
            validSurcharge(tipPriceSurchargeList, list, serviceCode, zone,Constants.PEAK_RESIDENTIAL_CHARGE);
        }

        // 处理峰值的常规附加处理费用
        list = groupedByItem.get(Constants.PEAK_ADDITIONAL_HANDLING_CHARGE);
        validSurcharge(tipPriceSurchargeList, list, serviceCode, zone,Constants.PEAK_ADDITIONAL_HANDLING_CHARGE);
    }




    /**
     * 根据给定的条件获取距离项
     *
     * @param groupedByItem 按项目分组的映射
     * @param defaultItem  默认项
     * @param isResidential 是否为住宅地址
     * @return 距离项
     */
    private String getDistanceItem(Map<String, List<PriceSurcharge>> groupedByItem, String defaultItem, Boolean isResidential) {
        String distanceItem;
        List<PriceSurcharge> remote1 = groupedByItem.get(defaultItem);
        if (remote1 == null) {
            distanceItem = isResidential? Constants.DAS_REMOTE_RESIDENTIAL : Constants.DAS_REMOTE_COMMERCIAL;
        } else {
            distanceItem = defaultItem;
        }
        return distanceItem;
    }

    /**
     * 生效一条
     * @param tipPriceSurchargeList
     * @param priceSurchargeList
     */
    @Override
    public void validSurcharge(List<PriceSurcharge> tipPriceSurchargeList, List<PriceSurcharge> priceSurchargeList,String serviceCode,
                                String zone,String item) {
        if (null!=priceSurchargeList){
            //符合服务的
            List<PriceSurcharge> collect;
            if (serviceCode!=null){

                //符合服务的
             collect = priceSurchargeList.stream()
                        .filter(surcharge -> SurchargeTool.containsServiceCode(surcharge.getServiceCodes(), serviceCode))
                        .collect(Collectors.toList());
            }else {
                collect=priceSurchargeList;
            }
            List<PriceSurcharge> last;
            if (zone!=null){
                //要分zone
                last   = collect.stream()
                        .filter(surcharge -> SurchargeTool.findByZone(surcharge.getZones(), zone))
                        .collect(Collectors.toList());
            }else {
                last=collect;
            }

            //服务能对上
            if (last.size()!=0) {
                tipPriceSurchargeList.add(last.get(0));
            }
        }else {
            log.info(serviceCode+"没配"+item+"附加费项");
        }
    }


    /**
     * 是否超远 标远1 超远2 遥远Remote US 48 Zip 3 ak 4 hi 5  0是正常
     * 偏远地区
     * 要分住宅和商业地址收费
     * DAS：Delivery Area Surcharge
     * US 48 Zip DAS Extended 表单
     * @param zipCode 邮编
     * @return true 存在  false 不存在
     */
    public Integer checkDistance(String zipCode) {
        // 首先检查 US 48 Zip DAS Extended 表单数据
        List<String> dasZipCodeList = redisTemplate.opsForList().range(RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.DAS_ZIP_CODE_PREFIX, 0, -1);
        if (containsZipCode(dasZipCodeList, zipCode)) {
            return 2;
        }

        // 检查 US 48 Zip 表单数据
        List<String> us48ZipCodeList = redisTemplate.opsForList().range(RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.US_48_ZIP_CODE_PREFIX, 0, -1);
        if (containsZipCode(us48ZipCodeList, zipCode)) {
            return 1;
        }

        // 检查 HI - 夏威夷 表单数据
        List<String> hiZipCodeList = redisTemplate.opsForList().range(RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.HI_ZIP_CODE_PREFIX, 0, -1);
        if (containsZipCode(hiZipCodeList, zipCode)) {
            return 5;
        }

        // 检查 AK - 阿拉斯加 表单数据
        List<String> akZipCodeList = redisTemplate.opsForList().range(RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.AK_ZIP_CODE_PREFIX, 0, -1);
        if (containsZipCode(akZipCodeList, zipCode)) {
            return 4;
        }

        // 检查 Remote US 48 Zip 表单数据
        List<String> easZipCodeList = redisTemplate.opsForList().range(RedisKeyConstants.ZIP_CODE_PREFIX + RedisKeyConstants.EAS_ZIP_CODE_PREFIX, 0, -1);
        if (containsZipCode(easZipCodeList, zipCode)) {
            return 3;
        }

        return 0;
    }
    /**
     * 检查给定的邮编列表是否包含指定的邮编
     * @param zipCodeList 邮编列表
     * @param zipCode 要检查的邮编
     * @return true 包含  false 不包含
     */
    private boolean containsZipCode(List<String> zipCodeList, String zipCode) {

        if (Optional.ofNullable(zipCodeList).orElse(Lists.newArrayList())
                .stream().anyMatch(item -> item.equals(zipCode))) {
            return true;
        }else {

            return false;
        }
    }
}
